﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Lucene.Net.Index;
using LuceneStore = Lucene.Net.Store;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Cn;
using Lucene.Net.Search;
using Lucene.Net.Search.Highlight;
using Lucene.Net.Documents;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Analysis.PanGu;
using Lucene.Net.Search.Vectorhighlight;

namespace XMK.Utils
{
    public class LuceneAccess
    {
        private string _indexPath;

        /// <summary>
        /// 默认采用 ChineseAnalyzer
        /// </summary>
        /// <param name="indexPath"></param>
        public LuceneAccess(string indexPath)
            : this(indexPath, new ChineseAnalyzer())
        {
            
        }

        public LuceneAccess(string indexPath, Analyzer indexAnalyzer)
        {
            _indexPath = indexPath;
            _luceneAnalyzer = indexAnalyzer;
        }

        public LuceneAccess(string indexPath, LuceneAnalyzerType analyzerType)
        {
            _indexPath = indexPath;
            switch (analyzerType)
            {
                
                case LuceneAnalyzerType.Standard:
                    _luceneAnalyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30);
                    break;
                case LuceneAnalyzerType.Pangu:
                    _luceneAnalyzer = new PanGuAnalyzer();
                    break;
                case LuceneAnalyzerType.Chinese:
                default:
                    _luceneAnalyzer = new ChineseAnalyzer();
                    break;
            }
        }

        private Analyzer _luceneAnalyzer;
        public Analyzer LuceneAnalyzer
        {
            get
            {
                if (_luceneAnalyzer == null)
                {
                    _luceneAnalyzer = new ChineseAnalyzer();
                }
                return _luceneAnalyzer;
            }
            set
            {
                _luceneAnalyzer = value;
            }
        }

        static Dictionary<string, IndexSearcher> IndexSearchesDict;

        static object lockObj = new object();

        private System.IO.DirectoryInfo _sysDirectory = null;
        /// <summary>
        /// 索引在硬盘上的目录
        /// </summary>
        public System.IO.DirectoryInfo SysDirectory
        {
            get
            {
                if (_sysDirectory == null)
                {
                    if (!System.IO.Directory.Exists(_indexPath))
                        _sysDirectory = System.IO.Directory.CreateDirectory(_indexPath);
                    else _sysDirectory = new System.IO.DirectoryInfo(_indexPath);
                }
                return _sysDirectory;
            }
        }

        private LuceneStore.Directory _luceneDirectory = null;
        /// <summary>
        /// Lucene.Net的目录-参数
        /// </summary>
        public LuceneStore.Directory LuceneDirectory
        {
            get
            {
                if (_luceneDirectory == null)
                    _luceneDirectory = LuceneStore.FSDirectory.Open(SysDirectory);
                return _luceneDirectory;
            }
        }

        public IndexWriter GetIndexWriter()
        {
            var exsit = !IndexReader.IndexExists(LuceneDirectory);

            return new IndexWriter(LuceneDirectory,
                LuceneAnalyzer, exsit, IndexWriter.MaxFieldLength.UNLIMITED);

        }

        private IndexReader _indexReader;

        public IndexReader LuIndexReader
        {
            get
            {
                if (_indexReader == null)
                {
                    _indexReader = IndexReader.Open(LuceneDirectory, true);
                }
                return _indexReader;
            }
        }

        public IndexSearcher GetIndexSearcher()
        {

            lock (lockObj)
            {
                if (IndexSearchesDict == null)
                {
                    IndexSearchesDict = new Dictionary<string, IndexSearcher>();
                }

                if (!IndexSearchesDict.Keys.Contains(_indexPath))
                {
                    System.IO.DirectoryInfo info = new System.IO.DirectoryInfo(_indexPath);

                    IndexSearcher searcher = new IndexSearcher(LuceneDirectory, true);

                    IndexSearchesDict.Add(_indexPath, new IndexSearcher(LuceneDirectory, true));
                }

                return IndexSearchesDict[_indexPath];

            }
        }

        
        
        /// <summary>
        /// 获取高亮结果
        /// 该方法要求构建索引时使用 Field.TermVector.WITH_POSITIONS_OFFSETS 来构造 Field
        /// key-文档ID
        /// value-高亮文本
        /// </summary>
        /// <param name="query">查询</param>
        /// <param name="docIdList">文档ID列表</param>
        /// <param name="fieldName">字段名称</param>
        /// <param name="preTags">高亮前缀</param>
        /// <param name="postTags">高亮后缀</param>
        /// <param name="maxChar">最大返回文字数</param>
        /// <returns></returns>
        public Dictionary<int,string> GetHighLightDict(LuceneQuery query, List<int> docIdList, string fieldName,
            List<string> preTags, List<string> postTags, int maxChar)
        {
            Dictionary<int, string> highDict = new Dictionary<int, string>();
            if (query == null) return highDict;
            if (preTags == null || preTags.Count == 0)
                preTags = BaseFragmentsBuilder.COLORED_PRE_TAGS.ToList();
            if (postTags == null || postTags.Count == 0)
                postTags = BaseFragmentsBuilder.COLORED_POST_TAGS.ToList();
            FragListBuilder fragListBuilder = new SimpleFragListBuilder(); ;
            FragmentsBuilder fragmentBuilder = new ScoreOrderFragmentsBuilder(
                            preTags.ToArray(),
                            postTags.ToArray());
            FastVectorHighlighter highlighter = new FastVectorHighlighter(true,
                true, fragListBuilder, fragmentBuilder);
            FieldQuery fieldQuery = highlighter.GetFieldQuery(query.LuQuery);

            foreach (int docId in docIdList)
            {
                highDict[docId] = highlighter.GetBestFragment(fieldQuery,
                        LuIndexReader, docId, fieldName, maxChar); 
            }

            return highDict;
        }

        /// <summary>
        /// 盘古分词获取高亮
        /// key-文档ID
        /// value-高亮后的文本
        /// </summary>
        /// <param name="query"></param>
        /// <param name="docIdList"></param>
        /// <param name="fieldName"></param>
        /// <param name="preTag"></param>
        /// <param name="postTag"></param>
        /// <returns></returns>
        public Dictionary<int, string> GetHighLightDictPangu(LuceneQuery query, List<int> docIdList, string fieldName, 
            string preTag, string postTag)
        {
            Dictionary<int, string> resultDict = new Dictionary<int, string>();
            IndexSearcher search = GetIndexSearcher();
            if (query == null)
            {
                foreach (var item in docIdList)
                {
                    Document doc = search.Doc(item);
                    resultDict[item] = doc.Get(fieldName);
                }
                return resultDict;
            }
            Highlighter highlighter = new Highlighter(new QueryScorer(query.LuQuery));
            highlighter.TextFragmenter = new SimpleFragmenter(100);
            
            Analyzer highanalyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30);
            foreach (var item in docIdList)
            {
                Document doc = search.Doc(item);
                string cont = doc.Get(fieldName);
                string file_ext = doc.Get(fieldName).Substring(doc.Get(fieldName).LastIndexOf(".") + 1);
                if (cont.Length > 45)
                {
                    cont = cont.Substring(0, 45) + "..." + file_ext;
                }
               
                Lucene.Net.Analysis.TokenStream titkeStream = highanalyzer.TokenStream(fieldName, new System.IO.StringReader(cont));
                resultDict[item] = highlighter.GetBestFragments(titkeStream, cont, 0, "..").Replace("<B>", preTag).Replace("</B>", postTag);
            }
            return resultDict;
        }

        /// <summary>
        /// 检索
        /// </summary>
        /// <param name="query">检索条件</param>
        /// <param name="pageIndex">页号</param>
        /// <param name="pageSize">每页条数</param>
        /// <param name="recordCount">总条数</param>
        /// <returns></returns>
        public int[] ExecuteQuery(LuceneQuery query,
            int pageIndex, int pageSize, out int recordCount)
        {
            TopScoreDocCollector collector = TopScoreDocCollector.Create(pageIndex * pageSize, false);
            IndexSearcher searcher = GetIndexSearcher();
            searcher.Search(query == null ? null : query.LuQuery, collector);
            if (collector == null || collector.TotalHits == 0)
            {
                recordCount = 0;
                return null;
            }
            int start = pageSize * (pageIndex - 1);
            ScoreDoc[] hits = collector.TopDocs(start, pageSize).ScoreDocs;
            recordCount = collector.TotalHits;
            if (hits == null || hits.Count() == 0)
            {
                recordCount = 0;
                return null;
            }
            return hits.Select(m=>m.Doc).ToArray();
        }

        /// <summary>
        /// 分页排序查询
        /// </summary>
        /// <param name="query">查询条件</param>
        /// <param name="sort">排序</param>
        /// <param name="pageIndex">页号</param>
        /// <param name="pageSize">每页条数</param>
        /// <param name="recordCount">总条数</param>
        /// <returns></returns>
        public int[] ExecuteQuery(LuceneQuery query, LuceneSort sort,
            int pageIndex, int pageSize, out int recordCount)
        {
            IndexSearcher searcher = GetIndexSearcher();
            TopFieldDocs topDocs =
                searcher.Search(query == null ? null : query.LuQuery, null, pageIndex * pageSize, sort.LuSort);
            if (topDocs == null || topDocs.TotalHits == 0)
            {
                recordCount = 0;
                return null;
            }

            ScoreDoc[] hits = topDocs.ScoreDocs;
            recordCount = topDocs.TotalHits;
            if (hits == null || hits.Count() == 0)
            {
                recordCount = 0;
                return null;
            }
            List<int> scoreDocList = new List<int>();
            int start = (pageIndex - 1) * pageSize;
            int end = pageIndex * pageSize;
            if (end > recordCount)
                end = recordCount;

            for (int i = start; i < end; i++)
            {
                scoreDocList.Add(hits[i].Doc);
            }
            return scoreDocList.ToArray();
        }

        /// <summary>
        /// 删除索引
        /// </summary>
        /// <param name="query"></param>
        public bool ExecuteDelete(LuceneQuery query)
        {
            bool indexOK = true;
            IndexWriter writer = GetIndexWriter();
            try
            {
                writer.DeleteDocuments(query.LuQuery);
            }
            catch (System.Exception ex)
            {
                indexOK = false;
            }
            finally
            {
                writer.Optimize();
                writer.Dispose();
            }
            return indexOK;
        }

    }
}
